﻿#region Using directives

using System;
using System.Text;
using System.CodeDom;
using System.Collections.Generic;

using Weazel.Gezel.Conversions.CodeGeneration;

#endregion

namespace Weazel.Gezel.Conversions.Model
{
  public class SimulatorType : Type
	{
		#region Fields and properties
		private Method runMethod;
		private Method dfsVisitMethod;
		private Method initializeMethod;

    private Field cyclesField;
    private Field currentCycleField;
    public Field CurrentCycleField
    {
      get
      {
        return currentCycleField;
      }
    }

    private Field outportListField;
    public Field OutportListField
    {
      get
      {
        return outportListField;
      }
    }

    private Field sortedOutportListField;
    public Field SortedOutportListField
    {
      get
      {
        return sortedOutportListField;
      }
		}

		#endregion

		public SimulatorType(SoftwareModel softwareModel) : base("Simulator", softwareModel)
    {
			initializeMethod = AddStaticMethod("Initialize", "Void");

			addFields(softwareModel);
			buildInitializeMethod();			
      buildRunMethod();
      buildDfsVisitMethod();
    }   

    private void addFields(SoftwareModel softwareModel)
    {
			CodeTypeReferenceExpression simulatorTypeRef = new CodeTypeReferenceExpression("Simulator");

      foreach (ControllerType controllerType in softwareModel.ControllerTypes.Values)
      {
        Field f = AddStaticField(controllerType.InstanceName, controllerType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name), 
						initExpression
					)
				); 
      }

      foreach (OutPortType outportType in softwareModel.OutportTypes.Values)
      {
        Field f = AddStaticField(outportType.InstanceName, outportType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name),
						initExpression
					)
				);
      }

      foreach (InPortType inportType in softwareModel.InportTypes.Values)
      {
        Field f = AddStaticField(inportType.InstanceName, inportType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name),
						initExpression
					)
				);
      }


      foreach (RegisterType registerType in softwareModel.RegisterTypes.Values)
      {
        Field f = AddStaticField(registerType.InstanceName, registerType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name),
						initExpression
					)
				);
      }

      foreach (SignalType signalType in softwareModel.SignalTypes.Values)
      {
        Field f = AddStaticField(signalType.InstanceName, signalType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name),
						initExpression
					)
				);
      }

      foreach (TracerType tracerType in softwareModel.TracerTypes)
      {
        Field f = AddStaticField(tracerType.InstanceName, tracerType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name),
						initExpression
					)
				);
      }

      foreach (DisplayType displayType in softwareModel.DisplayTypes.Values)
      {
        Field f = AddStaticField(displayType.InstanceName, displayType.Name);
				CodeObjectCreateExpression initExpression = new CodeObjectCreateExpression(f.Type);
				initializeMethod.Statements.Add(
					new CodeAssignStatement(
						new CodeMethodReferenceExpression(simulatorTypeRef, f.Name),
						initExpression
					)
				);
      }

      cyclesField = AddStaticField("Cycles", "Int32");
      currentCycleField = AddStaticField("CurrentCycle", "Int32");


			if (softwareModel.Language == TargetLanguage.IsoCpp)
			{
        outportListField = AddStaticField("OutportList", "OutportDynamicList");
        sortedOutportListField = AddStaticField("SortedOutportList", "OutportDynamicList");
			}
			else
			{
				outportListField = AddStaticField("OutportList", "List<IOutport>");
				sortedOutportListField = AddStaticField("SortedOutportList", "List<IOutport>");
			}
    }

    private void buildInitializeMethod()
    {						
			CodeFieldReferenceExpression outportListField = 
				new CodeFieldReferenceExpression(
					new CodeTypeReferenceExpression("Simulator"), 
					"OutportList"
				);
			CodeObjectCreateExpression createExpression;
			if (softwareModel.Language != TargetLanguage.IsoCpp)
			{
				createExpression =
				new CodeObjectCreateExpression(
					"List<IOutport>",
					new CodePrimitiveExpression(softwareModel.OutportTypes.Count)
				);
			}
			else
			{
				// don't specify initial capacity
				createExpression =
				new CodeObjectCreateExpression(
          "OutportDynamicList"
				);
			}
			
			CodeAssignStatement assignOutportList = 
				new CodeAssignStatement(outportListField, createExpression);
			initializeMethod.Statements.Add(assignOutportList);

			CodeFieldReferenceExpression sortedOutportListField = 
				new CodeFieldReferenceExpression(
					new CodeTypeReferenceExpression(
						"Simulator"
					), 
					"SortedOutportList"
				);
			CodeAssignStatement assignSortedOutportList = 
				new CodeAssignStatement(sortedOutportListField, createExpression);
			initializeMethod.Statements.Add(assignSortedOutportList);


			initializeMethod.Statements.Add(new CodeSnippetStatement());

      initializeMethod.Statements.Add(new CodeCommentStatement("call initializers on the entities in simulation"));
      foreach (Field field in Fields.Values)
      {

        if (field.Type.BaseType.StartsWith("Register_") ||
						field.Type.BaseType.StartsWith("Signal_") ||
						field.Type.BaseType.StartsWith("OutPort_") ||
            field.Type.BaseType.StartsWith("IpBlockInPort_") ||
						field.Type.BaseType.StartsWith("Trace_") ||
            field.Type.BaseType.StartsWith("Display_") ||
						field.Type.BaseType.StartsWith("Controller_"))
        {
          // add code to call intialize method
					CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), field.Name);
					CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression(fieldReference, "Initialize");
					initializeMethod.Statements.Add(methodInvoke);
        }
      }
    }

    private void buildRunMethod()
    {
			runMethod = AddStaticMethod("Run", "Void");

			// variable declarations
			runMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(Int32), "i", new CodePrimitiveExpression(0)));
			if (softwareModel.Language != TargetLanguage.IsoCpp)
			{
				runMethod.Statements.Add(new CodeVariableDeclarationStatement("IOutport", "outport"));
			}
			else // C++ special case
			{
				runMethod.Statements.Add(new CodeVariableDeclarationStatement("IOutport*", "outport"));
			}

			// references to common variables
			CodeTypeReferenceExpression simulatorType = new CodeTypeReferenceExpression("Simulator");
			CodeVariableReferenceExpression iRef =  new CodeVariableReferenceExpression("i");
			CodeVariableReferenceExpression outportRef = new CodeVariableReferenceExpression("outport");

			CodeFieldReferenceExpression sortedOutportListField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), "SortedOutportList");
			CodeFieldReferenceExpression outportListField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), "OutportList");
			CodeFieldReferenceExpression currentCycleField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), "CurrentCycle");
			CodeFieldReferenceExpression cyclesField = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), "Cycles");

			// while(CurrentCycle < Cycles) ...
			CodeIterationStatement loop = new CodeIterationStatement();
			
			loop.TestExpression = new CodeBinaryOperatorExpression(currentCycleField, CodeBinaryOperatorType.LessThan, cyclesField);
			loop.IncrementStatement = new CodeSnippetStatement();
			loop.InitStatement = new CodeSnippetStatement();

      // evaluate controllers
      loop.Statements.Add(new CodeCommentStatement("evaluate controllers"));
      foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("Controller"))
        {
          // skip hardwired controllers, they do not need to be evaluated
          // since they never changes state
          //if ((field.Type as ControllerType).Controller.Type == Weazel.Gezel.Model.Controller.ControllerType.Hardwired)
          //	continue;

          CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Simulator"), field.Name);
          loop.Statements.Add(new CodeMethodInvokeExpression(fieldRef, "Evaluate"));
        }
      }
      loop.Statements.Add(Statement.Empty);

     
			if (softwareModel.Language != TargetLanguage.IsoCpp)
			{
				//
				// topological sort outports
				//

				// for(int i = 0; i < OutportList.Count; i++)
				CodeIterationStatement dfsSortLoop = new CodeIterationStatement();
				dfsSortLoop.InitStatement = new CodeAssignStatement(iRef, new CodePrimitiveExpression(0));
				dfsSortLoop.TestExpression = new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.LessThan, new CodePropertyReferenceExpression(outportListField, "Count"));
				dfsSortLoop.IncrementStatement = new CodeAssignStatement(iRef, new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));
				dfsSortLoop.Statements.Add(new CodeAssignStatement(outportRef, new CodeArrayIndexerExpression(outportListField, iRef)));

				// if(outport.Color == Color.White)
				CodeConditionStatement dfsConditionStatement = new CodeConditionStatement();
				dfsConditionStatement.Condition = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(outportRef, "_Color"), CodeBinaryOperatorType.IdentityEquality, new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("Color"), "White"));
				dfsConditionStatement.TrueStatements.Add(new CodeMethodInvokeExpression(simulatorType, "DfsVisit", new CodeDirectionExpression(FieldDirection.Ref, outportRef)));
				dfsSortLoop.Statements.Add(dfsConditionStatement);
				loop.Statements.Add(dfsSortLoop);
				loop.Statements.Add(Statement.Empty);

				//
				// evaluate outports
				//
				CodeIterationStatement outportEvalLoop = new CodeIterationStatement();
				outportEvalLoop.InitStatement = new CodeAssignStatement(iRef, new CodePrimitiveExpression(0));
				outportEvalLoop.TestExpression = new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.LessThan, new CodePropertyReferenceExpression(sortedOutportListField, "Count"));
				outportEvalLoop.IncrementStatement = new CodeAssignStatement(iRef, new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));
				outportEvalLoop.Statements.Add(new CodeMethodInvokeExpression(new CodeArrayIndexerExpression(sortedOutportListField, iRef), "Evaluate"));
				loop.Statements.Add(outportEvalLoop);

				loop.Statements.Add(Statement.Empty);

			}
			else // C++ special case
			{
				//
				// topological sort outports
				//

				loop.Statements.Add(new CodeSnippetStatement("      for (i = 0; i < OutportList->size(); i++)"));
				loop.Statements.Add(new CodeSnippetStatement("      {"));
				loop.Statements.Add(new CodeSnippetStatement("			  outport = Simulator::OutportList->at(i);"));
				loop.Statements.Add(new CodeSnippetStatement("			  if (outport->_Color == Color::White)"));
				loop.Statements.Add(new CodeSnippetStatement("        {"));
				loop.Statements.Add(new CodeSnippetStatement("          Simulator::DfsVisit(&(outport));"));
				loop.Statements.Add(new CodeSnippetStatement("        }"));
				loop.Statements.Add(new CodeSnippetStatement("      }"));
				loop.Statements.Add(Statement.Empty);

				//
				// evaluate outports
				//
				loop.Statements.Add(new CodeSnippetStatement("      for (i = 0; i < SortedOutportList->size(); i++)"));
				loop.Statements.Add(new CodeSnippetStatement("      {"));
				loop.Statements.Add(new CodeSnippetStatement("          Simulator::SortedOutportList->at(i)->Evaluate();"));
				loop.Statements.Add(new CodeSnippetStatement("      }"));
				loop.Statements.Add(Statement.Empty);
			}			

			//
      // evaluate registers
			//
			loop.Statements.Add(new CodeCommentStatement("evaluate registers"));
      foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("Register"))
        {
					CodeFieldReferenceExpression registerField = 
						new CodeFieldReferenceExpression(
							new CodeTypeReferenceExpression("Simulator"), 
							field.Name
						);
					loop.Statements.Add(new CodeMethodInvokeExpression(registerField, "Evaluate"));
        }
      }
			loop.Statements.Add(Statement.Empty);

      //
      // evaluate ipblock inports
			//
      loop.Statements.Add(new CodeCommentStatement("evaluate ipblock inports"));
      foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("IpBlockInPort_"))
        {
          CodeFieldReferenceExpression ipInPortField =
            new CodeFieldReferenceExpression(
              new CodeTypeReferenceExpression("Simulator"),
              field.Name
            );
          loop.Statements.Add(new CodeMethodInvokeExpression(ipInPortField, "Evaluate"));
        }
      }
      loop.Statements.Add(Statement.Empty);

      //
      // run directives 
      loop.Statements.Add(new CodeCommentStatement("evaluate directives"));
      foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("Display_"))
        {
          CodeFieldReferenceExpression displayField =
            new CodeFieldReferenceExpression(
              new CodeTypeReferenceExpression("Simulator"),
              field.Name
            );
          loop.Statements.Add(new CodeMethodInvokeExpression(displayField, "Evaluate"));
        }
      }
      loop.Statements.Add(Statement.Empty);

			//
      // run tracers
			//

			loop.Statements.Add(new CodeCommentStatement("run tracers"));
      if(softwareModel.Language == TargetLanguage.IsoCpp)
        loop.Statements.Add(new CodeSnippetStatement("#ifdef _MSC_VER"));

      foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("Trace"))
        {
					CodeFieldReferenceExpression tracerField = 
						new CodeFieldReferenceExpression(
							new CodeTypeReferenceExpression("Simulator"), 
							field.Name
						);
					loop.Statements.Add(new CodeMethodInvokeExpression(tracerField, "Trace"));
        }
      }
      loop.Statements.Add(Statement.Empty);

      if (softwareModel.Language == TargetLanguage.IsoCpp)
      {
        loop.Statements.Add(new CodeSnippetStatement("#endif // _MSC_VER"));
        loop.Statements.Add(Statement.Empty);
      }

			//
      // update register values
			//

			loop.Statements.Add(new CodeCommentStatement("update register values"));
      foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("Register"))
        {
					CodeFieldReferenceExpression registerField =  
						new CodeFieldReferenceExpression(
							new CodeTypeReferenceExpression("Simulator"), 
							field.Name
						);
					loop.Statements.Add(new CodeMethodInvokeExpression(registerField, "Tick"));
        }
      }
			loop.Statements.Add(Statement.Empty);

			loop.Statements.Add(new CodeCommentStatement("prepare for next cycle"));
			
			
			

			if (softwareModel.Language != TargetLanguage.IsoCpp)
			{
				// foreach (IOutport outport in OutportList)
				CodePropertyReferenceExpression sortedOutportListCountPropertyRef =
					new CodePropertyReferenceExpression(sortedOutportListField, "Count");
				CodeIterationStatement resetLoop = new CodeIterationStatement();
				resetLoop.InitStatement = new CodeAssignStatement(iRef, new CodePrimitiveExpression(0));
				resetLoop.IncrementStatement = new CodeAssignStatement(iRef, new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));
				resetLoop.TestExpression = new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.LessThan, sortedOutportListCountPropertyRef);

				// outport.Color = Color.White;
				CodeArrayIndexerExpression arrayIndexer =
					new CodeArrayIndexerExpression(
						sortedOutportListField,
						iRef
					);
				CodePropertyReferenceExpression colorProperty =
					new CodePropertyReferenceExpression(arrayIndexer, "_Color");
				CodeFieldReferenceExpression colorWhiteRef =
					new CodeFieldReferenceExpression(
						new CodeTypeReferenceExpression("Color"),
						"White"
					);

				resetLoop.Statements.Add(new CodeAssignStatement(colorProperty, colorWhiteRef));
				loop.Statements.Add(resetLoop);
			}
			else // C++ special case
			{
				loop.Statements.Add(new CodeSnippetStatement("      for (i = 0; i < SortedOutportList->size(); i++)"));
				loop.Statements.Add(new CodeSnippetStatement("      {"));
				loop.Statements.Add(new CodeSnippetStatement("        SortedOutportList->at(i)->_Color = Color::White;"));
				loop.Statements.Add(new CodeSnippetStatement("      }"));
				
			}
			
			
			loop.Statements.Add(Statement.Empty);

			if (softwareModel.Language != TargetLanguage.IsoCpp)
			{
				loop.Statements.Add(new CodeMethodInvokeExpression(outportListField, "Clear"));
				loop.Statements.Add(new CodeMethodInvokeExpression(sortedOutportListField, "Clear"));
			}
			else // C++ special case
			{
				// emptying a OutportDynamicList is done with a call to clear()
				loop.Statements.Add(new CodeMethodInvokeExpression(outportListField, "clear"));
				loop.Statements.Add(new CodeMethodInvokeExpression(sortedOutportListField, "clear"));
			}
			loop.Statements.Add(new CodeAssignStatement(currentCycleField, new CodeBinaryOperatorExpression(currentCycleField, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1))));

			runMethod.Statements.Add(loop);

			foreach (Field field in Fields.Values)
      {
        if (field.Type.BaseType.StartsWith("Trace"))
        {
					runMethod.Statements.Add(
						new CodeMethodInvokeExpression(
							new CodeFieldReferenceExpression(
								new CodeTypeReferenceExpression("Simulator"), 
								field.Name
							), 
							"CleanUp"
						)
					);
        }
      }
    }

		private void buildDfsVisitMethod()
		{
			//
			// set up DvsVisit method
			//
			dfsVisitMethod = AddStaticMethod("DfsVisit", "Void");
			CodeParameterDeclarationExpression outputParameter =
				new CodeParameterDeclarationExpression("IOutport", "outport");
			outputParameter.Direction = FieldDirection.Ref;
			dfsVisitMethod.Parameters.Add(outputParameter);

			if (softwareModel.Language == TargetLanguage.IsoCpp)
			{
				buildCppDfsVisitMethod();
			}
			else
			{
				buildManagedDfsVisitMethod();
			}
		}

		/// <summary>
		/// Builds the recursive part of the depth first visit algorithm using a
		/// CodeDom object-graph.
		/// </summary>
		private void buildCppDfsVisitMethod()
		{
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  int i;"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  IOutport* child;"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  OutportDynamicList* children = (*outport)->GetDependentOutports();"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  (*outport)->_Color = Color::Gray;"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  for (i = 0; i < children->size(); i = i + 1)"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  {"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    child = children->at(i);"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    if (child->_Color == Color::White)"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    {"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("      Simulator::DfsVisit(&(child));"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    }"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    else if(child->_Color == Color::Gray)"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    {"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("      throw new ApplicationException(\"Signal in combinatorial loop.\");"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("    }"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  }"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  (*outport)->_Color = Color::Black;"));
			dfsVisitMethod.Statements.Add(new CodeSnippetStatement("  Simulator::SortedOutportList->push_back(*outport);"));
		}

		/// <summary>
		/// Builds the recursive part of the depth first visit algorithm using a
		/// CodeDom object-graph.
		/// 
		/// NOTE: we have to access Dfs Color through interface to be object oriented
		///				even though it is not as efficient as direct access to the underlying
		///				field.
		/// </summary>
		private void buildManagedDfsVisitMethod()
		{		
			// int i;
			dfsVisitMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(Int32), "i"));

			// IOutport child
			dfsVisitMethod.Statements.Add(new CodeVariableDeclarationStatement("IOutport", "child"));

			// IOutport[] children = outport.GetDependentOutports();
			dfsVisitMethod.Statements.Add(
				new CodeVariableDeclarationStatement(
					new CodeTypeReference("IOutport", 1), 
					"children", 
					new CodeMethodInvokeExpression(
						new CodeArgumentReferenceExpression("outport"), 
						"GetDependentOutports"
					)
				)
			);

			// outport.Color = Color.Gray;
			dfsVisitMethod.Statements.Add(
				new CodeAssignStatement(
					new CodePropertyReferenceExpression(
						new CodeArgumentReferenceExpression("outport"), 
						"_Color"
					), 
					new CodeFieldReferenceExpression(
						new CodeTypeReferenceExpression("Color"), 
						"Gray"
					)
				)
			);

			CodeVariableReferenceExpression iVariable = 
				new CodeVariableReferenceExpression("i");
			CodePropertyReferenceExpression childrenLengthProperty = 
				new CodePropertyReferenceExpression(
					new CodeVariableReferenceExpression("children"), 
					"Length"
				);

			// for(int i = 0; i < children.Length; i++)
			CodeIterationStatement loop = new CodeIterationStatement();
			
			loop.InitStatement = 
				new CodeAssignStatement(iVariable, new CodePrimitiveExpression(0));
			
			loop.TestExpression = 
				new CodeBinaryOperatorExpression(
					iVariable, 
					CodeBinaryOperatorType.LessThan, 
					childrenLengthProperty
				);
			
			loop.IncrementStatement = 
				new CodeAssignStatement(
					iVariable, 
					new CodeBinaryOperatorExpression(
						iVariable, 
						CodeBinaryOperatorType.Add, 
						new CodePrimitiveExpression(1)
					)
				);

			CodeVariableReferenceExpression childVariable = 
				new CodeVariableReferenceExpression("child");
			CodeArrayIndexerExpression childrenIndexer = 
				new CodeArrayIndexerExpression(
					new CodeVariableReferenceExpression("children"), 
					iVariable
				);

			loop.Statements.Add(
				new CodeAssignStatement(childVariable, childrenIndexer)
			);

			// if(child.Color == Color.White)
			CodeConditionStatement colorBranch = new CodeConditionStatement();
			CodePropertyReferenceExpression childColorProperty = 
				new CodePropertyReferenceExpression(childVariable, "_Color");
			
			CodeFieldReferenceExpression whiteColorField = 
				new CodeFieldReferenceExpression(
					new CodeTypeReferenceExpression("Color"), 
					"White"
				);

			colorBranch.Condition = 
				new CodeBinaryOperatorExpression(
					childColorProperty, 
					CodeBinaryOperatorType.IdentityEquality, 
					whiteColorField
				);

			// DfsVisit(ref child);
			colorBranch.TrueStatements.Add(
				new CodeMethodInvokeExpression(
					new CodeTypeReferenceExpression("Simulator"), 
					"DfsVisit", 
					new CodeDirectionExpression(
						FieldDirection.Ref, 
						childVariable
					)
				)
			);

      CodeFieldReferenceExpression grayColorField =
        new CodeFieldReferenceExpression(
          new CodeTypeReferenceExpression("Color"),
          "Gray"
        );

      CodeConditionStatement combLoopCheck = new CodeConditionStatement();
      combLoopCheck.Condition = new CodeBinaryOperatorExpression(
					childColorProperty, 
					CodeBinaryOperatorType.IdentityEquality, 
					grayColorField
				);

			// throw new ApplicationException("Signal in combinatorial loop.");
			combLoopCheck.TrueStatements.Add(
				new CodeThrowExceptionStatement(
					new CodeObjectCreateExpression(
						"ApplicationException", 
						new CodePrimitiveExpression("Signal in combinatorial loop.")
					)
				)
			);

      colorBranch.FalseStatements.Add(combLoopCheck);

			loop.Statements.Add(colorBranch);
			dfsVisitMethod.Statements.Add(loop);

			// outport.Color = Color.Black;
			dfsVisitMethod.Statements.Add(
				new CodeAssignStatement(
					new CodePropertyReferenceExpression(
						new CodeArgumentReferenceExpression("outport"),
						"_Color"
					),
					new CodeFieldReferenceExpression(
						new CodeTypeReferenceExpression("Color"),
						"Black"
					)
				)
			);

			// SortedOutportList.Add(outport);
			dfsVisitMethod.Statements.Add(
				new CodeMethodInvokeExpression(
					new CodeFieldReferenceExpression(
						new CodeTypeReferenceExpression("Simulator"), 
						"SortedOutportList"
					), 
					"Add", 
					new CodeVariableReferenceExpression("outport")
				)
			);
		}
  }
}
